<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <!-- 第一天.... -->
  <!-- 第二天 
    1. 认识环境变量文件 .env.xxxx 根据运行的终端命令不同 执行不同的环境变量文件 
    yarn dev .env.development 文件
    yarn build:prod .env.production 文件
    2. 利用环境变量文件中变量 替换 axios的基地址 process.env.VUE_APP_BASE_API  .env.development中VUE_APP_BASE_API = '/api'
    3. 准备了存储用户相关信息的vuex user模块 定义了action mutation token
    4. 封装登录的接口api api/user.js 并导出  注意点 return
    5. 在仓库的action中发送请求, 提交mutation  存储token到vuex中
    6. 利用utils/auth.js 本地持久化处理的方法  user模块中调用 实现token存储的持久化
    7. axios的响应拦截器 直接 扒一层数据
    8. axios的请求拦截器中 在请求头中统一挂载token
    9. axios的响应拦截器中统一的消息提示
    10. 在login/index.vue 登录成功做跳转  按钮的loading效果
    11. 输入错误密码 还能成功跳转到 首页 问题 => 在响应拦截器中 手动抛出错误的promise 阻止后续代码执行 
        try finally  按钮的loading状态
    12. 导航守卫+登录拦截(页面鉴权) 
       前置导航守卫 判断是否有token 有token 去登录页面 强制去首页  想去哪就去哪
                                  没有token 判断是否去白名单页面  是白名单 放行 反之 强制去登录
    13. 进度条 nprogress.start()  nprogress.done() 强制拦截后不会经过后置导航守卫 手动done掉
    14. 获取用户信息
       1. 封装了获取用户信息接口
       2. 在store/modules/user.js  定义action发送请求获取信息 定义mutation存储个人信息数据
       3. layout/navbar 组件 测试获取了个人信息
    15. 先获取个人信息 再放行进入   将获取个人信息的操作放在 前置导航守卫中获取 
    16. 解决每次切换页面频繁获取个人信息 全局getter 判断处理                                     
   -->
  <!-- 第三天
    1. 定义全局的getter, 渲染头像+名称
    2. 默认头像 字符串str.slice/substring 截取姓处理头像  小bug ?. 可选链操作符
    3. 退出
       1. 为按钮绑定点击事件 .native
       2. 确认消息提示
       3. 在store/user模块中定义 清除token和个人信息的mutation
       4. 在处理函数中调用mutation
       5. 跳转到登录页面
    4. token失效处理
       1. 响应拦截器的第二个处理函数中 判断状态码
       2. 在处理函数中调用退出的mutation
       3. 跳转到登录页面
    5. 修改密码
       1. 准备了对话框结构
       2. 准备了对话框表单和数据字段
       3. 表单校验规则
       4. 关闭的表单和校验规则的重置
       5. api/user封装更新密码的接口, 预校验通过后发送更新密码的请求 并清除token+个人信息 回到登录页面
    6. .sync修饰符
        方便了 父传子和子传父
        1. 父传子 :属性.sync="属性"
        2. 子组件props 接收
        3. 子传父 自定义事件类型是 update:属性, 值   
    7. 配置路由规则
       1. 测试组织页面的路由规则
       2. 拆分routers/modules 各个页面的路由规则模块
       3. 导入并放在constantRoutes数组中
       4. 拆分权限路由规则 和没有权限的路由规则数组 并合并展示    
  -->
  <!-- 第四天
   1. 梳理左侧sidebar组件(了解)
   2. 组织架构认识了tree组件 data="[{label: xx, children: 'children'}]" 
                           props="{label: 'name', children: 'child'}"
                           default-expand-all
   3. 使用作用域插槽定义左右布局 el-row > el-col  作用域插槽上解构 data数据 
   4. 封装接口 获取部门列表并展示
   5. 使用树形递归处理树形数据 
   6. 添加子部门
       1. 使用command方式绑定事件
       2. 抽离了add-depts子组件  父组件中定义控制对话框的布尔值 传递给子组件
       3. 准备表单结构
       4. 做表单的基本校验
       5. 部门名称+部门编码重复校验
          5.1 在父组件中存储一份原始的部门列表数据
          5.2 传递给子组件
          5.3 自定义校验规则中使用数组的find方法 
       6. 部门负责人列表的获取和渲染 
       7. 准备pid字段  父组件中点击的时候记录点击的data.id 父组件存储传递给子组件
       8. 封装新增部门的接口  发送请求 关闭对话框并重置表单 通知父组件重新渲染        
  
   -->
  <!-- 父子组件钩子函数执行顺序
       初始化阶段 父组件 beforeCreated => 父组件created => 组件的beforeMount => 子组件beforeCreated => 子组件created => 子组件beforeMount => 子组件的Mounted=> 父组件的mounted
       更新阶段  父组件beforeUpdate => 子组件 beforeUpdate => 子组件的updated=>父组件的updated
       销毁阶段  父组件beforeDestory => 子组件 beforeDestory => 子组件的destoryed=>父组件destoryed
       父组件如何监听到子组件执行到了某个钩子????????????
   -->
  <!-- 第五天
    1. 部门编辑
     1.1 展示对话框,并记录部门id 
     1.2 准备获取部门详情的接口, 在父组件中通过ref的方式调用子组件获取部门详情的方法  nextTick处理
     1.3 处理编辑状态下部门名称和部门编码的校验 部门详情数据的备份, 校验规则中补充分支 value是否===备份数据的name/code cb()
     1.4 准备更新的接口, 拆分确定按钮的处理函数 按需去发送新增/编辑的请求
     1.5 标题的处理 通过ref的方式设置标题
     1.6 先编辑再新增bug 需要在对话框关闭后 重新将form还原
    2. 删除部门
      2.1 确认消息提示
      2.2 准备删除部门的接口
      2.3 调用删除的接口 重新渲染 
    3. 全屏的loading效果 请求拦截器和响应拦截器中  
    4. 左侧sidebar的logo
    5. 角色页面
      5.1 搭建了页面基本结构 按钮 表格 分页组件
      5.2 获取了角色列表数据并渲染
      5.3 使用插槽 自定义了 启用列和操作列
      5.4 分页组件的功能
      5.5 新增角色
         1. 准备布尔值
         2. 准备对话框和表单结构+字段+校验规则
         3. 开关的组件渲染
         4. 封装接口 发送新增的请求
     6. 编辑操作
      1. 在list赋值之前 遍历角色列表添加isEdit字段
      2. 点击编辑按钮的时候 将当前行的isEdit设置为true
      3. 每一列中使用作用域插槽中 row.isEdit状态区分两种结构    
  -->
  <!-- 第六天
   1. 角色编辑
    1.1 角色编辑的回显 获取到角色数据后,再手动拼接处一个新的对象, 将这个对象v-model绑定给编辑状态下的表单元素上
    1.2 点击的取消 拼接的对象的数据还原
    1.3. 点击确定 发送编辑提交的请求 关闭编辑的状态  使用Object.assign 将更改后的数据 同步给row
   2. 角色的删除
    2.1 准备气泡弹窗 绑定确定事件的 @onConfirm 
    2.1 准备接口发送删除的请求 (如果只剩一条数据了 page--) 重新渲染  
   3. 员工页面结构搭建
      3.1 渲染左侧的树形结构数据 
      3.2 记录部门id params.departmentId 两个位置记录  默认记录 + 点击部门记录
      3.3 准备右侧表格+分页组件的结构
      3.2 准备接口 发送请求获取员工列表   两次获取  携带默认部门id获取  切换了部门id后获取一次
      3.3 存储员工列表 渲染表格
      3.4 分页组件功能  注意 当切换部门的时候 将page设置为1
      3.5 keyword关键词搜索
         1. 在params中新增keyword  将keyword绑定到 输入框上
         2. 为输入框绑定input事件 发送获取员工的请求  优化 非空  防抖  页码page = 1 
         3. 补充扩展 输入框 clreable 属性 + @clear事件  
         4. 补充扩展 部门列表较多 处理滚动条  el-scrollbar组件包裹left盒子 层叠样式
   -->
  <!-- 第七天 
      1. excel的导出 
         1. 导出按钮绑定点击事件
         2. 封装了导出excel接口, 返回的数据是blob类型 单独对这个接口 设置 responseType: 'blob'
         3. 响应拦截器中处理响应response.data是blob类型的判断   if (response.data instanceof Blob) return response.data
         4. 调用接口  使用 flieSaver.saveAs(数据, 文件.扩展名)
      2. excel导入
         1. 封装了弹窗子组件 子组件中 下载导入excel的模板   上传excel
         2. 下载导入excel的模板    封装了导出excel接口, 返回的数据是blob类型 单独对这个接口 设置 responseType: 'blob'  使用 flieSaver.saveAs(数据, 文件.扩展名)
         3. 梳理 input file 按钮配合实现的业务
            1. 隐藏file文件域
            2. 点击自己的按钮要实现文件的选择  触发file文件域的点击事件
            3. 选择文件 需要监听file文件域的change事件
            4. 获取选择的文件  console.log(e.target.files[0])
            5. 携带这个上传的文件对象 发生请求
            6. 清空当前文件域的value值
         4. 上传excel    
            1. 下载按钮处理file文件域的点击事件
            2. 在文件域上绑定change事件 
            3. 准备上传的接口
            4. 获取e.target.files[0] 并收集到FormData中
            5. 发送上传的请求
            6. 清空file的value值
         5.拖拽上传 
           1. dragenter dragover drop 配置阻止默认行为和事件冒泡 e.stopPropagation() e.preventDefault()
           2. drop对应的事件处理函数中 接收上传的文件对象 e.dataTransfer.files[0]
           3. 封装上传excel发送请求的业务为一个函数复用
         6. 删除员工
           6.1 绑定事件 准备气泡弹窗
           6.2 准备接口  发送请求  page-- 重新渲染
         7. 新增员工
          7.1 准备新增员工的组件和路由规则 并给新增员工按钮绑定点击事件 跳转到 /employee/detail
          7.2 准备结构+数据+校验规则 挨个关联  cv
          7.3 部门选择
            7.3.1 准备部门选择子组件 select-tree
            7.3.2 子组件中使用了级联选择器 el-cascader  :options="树形数据" :props="{label: 'name', value: 'id'}"
                  utils/index tranlateToTreeData  优化 有length 再加children树形  
            7.3.2 当选择了部门后要将选择的部门id记录(子传父) 
                 绑定change事件
                 发布自定义事假传递 最后一项部门id
                 父组件绑定自定义事件 接收id 并赋值给userInfo.departmentId     
           


  -->
  <!-- 第八天 
    1. 员工编辑
      1.1 改造了 detail对应的路由规则    path: 'detail/:id?', 
      1.2 如果路由地址中有id, 查询用户信息 数据的回显 改造了selectTree组件支持v-model
      1.3. 封装编辑提交的几口  有id走编辑提交的业务 ,没有id 新增业务
    2. 封装上传图像组件 
      2.1. 新建 image-upload组件  做了上传前的文件大小和文件类型的校验   
      2.2 为image-upload做了 v-model支持 userInfo.staffPhoto
      2.3 腾讯云cos 注册 认证 创建存储桶 开启了跨域  生成key+id 
      2.4 覆盖上传组件默认上传方式  :httt-request="fn"  腾讯云中复制上传简单对象的业务代码 将图像上传到腾讯云的存储桶中
      2.5 将腾讯云存储好返回的地址 同步给父组件
      2.6 (扩展) 上传进度条  el-progress组件 可选
    3. 权限页面  
      3.1. 准备权限页面的结构
      3.2 获取权限点 展示一级权限(页面访问权)
      3.3 表格树形展示 row-key="id" 为数据处理children属性 按需控制二级权限(按钮操作权) 添加按钮按需显示

      作业: 编辑  修改  新增


    4. 权限说明  员工  角色  权限   为员工分配角色  再为角色分配权限 
    5. 为员工分配角色 
     5.1 准备对话框
     5.2 获取启用的所有权限点  封装接口 
     5.3 获取当前点击用户的已有权限点  
     5.4 复选框组的 默认展示所有权限 并勾选已有权限点




   -->
  <!-- 
    所有演讲  组件通信 
      what (是什么)  why(为什么用它 好处) how(怎么用)  where(场景)  
    1. 它是什么? 组件与组件数据交互传递的方式
    2. 为什么要用? 组件之间的数据是独立的, 不能直接相互访问的, 当某个组件的数据需要在另一个组件中使用, 此时使用组件通信了
    3. 怎么用? 父传子 属性传递 props接收 子传父  $emit  ref 操作dom  vuex 状态管理工具  eventbus(vue3已废弃)  依赖注入  pinia 
    4. 场景: 简单数据 几个组件使用  父子   涉及到多组件共享  vuex  pinia 

    组件通信就是 组件与组件数据交互传递的方式, 为什么会涉及到组件通信呢? 组件之间的数据是独立的, 不能直接相互访问的, 当某个组件的数据需要在另一个组件中使用, 此时使用组件通信了
    常见的组件通信方式我知道的有这么几种 最常见的是父子组件通信 父传子 属性传递 props接收 子传父  $emit 发布自定事件 如果涉及的通信组件比较多, 直接上vuex/pinia了, vuex的使用
    定义共享数据  state  更改数据的唯一节点是mutations 涉及到异步 actions ,基于state中的数据派生的新数据  getters
    除了这些我还知道  eventbus(vue3已废弃)  依赖注入 简单聊一 eventbus 依赖注入是啥

    一些大项目 使用vuex pinia
   -->
  <!-- 第九天 
    先给用户分配角色  => 再给角色分配权限 
    1.先给用户分配角色
     1.1 准备为用户分配角色的接口
     1.2 携带用户的id 和选中的角色ids 发送分配角色的请求  关闭对话框
    2.给角色分配权限  
     2.1 准备为角色分配权限的对话框
     2.2 获取到所有的权限列表 并处理成树形数据展示  show-checkbox 展示复选框 check-strictly 断开父子关联关系
     2.3 勾选当前角色已有的权限
       2.3.1 封装接口 获取当前角色已有权限  默认勾选(方法 setCheckedKeys || 属性 default-checked-keys)
     2.4 为角色分配权限
        2.4.1  封装接口  发送请求 携带角色id和 勾选的权限点 通过getCheckedKeys()
    
    3.权限处理
    1. 静态路由规则 和动态路由规则的拆分  router/index.js
    2. 在用户登录后 获取到用户的权限信息, 根据用户的权限信息 过滤用户已有的权限页面
      2.1 permission.js  登录后  基于用户信息的menus 过滤筛选 asyncRoutes 
      2.2 通过路由 addRoutes 添加过滤出来的动态路由规则
      2.3 刷新白屏的bug  next(to.path)  处理 404页面 拼接
    3. 处理左侧菜单的显示问题 -使用vuex管理路由规则数据
      1.store/modules/user.js  routes: constantRoutes 
      2.获取用户信息完毕后(permission.js) 触发mutation 将过滤出来的数据 添加到 routes中
      3. sidebar/index.vue 访问vuex的路由规则的数据
    4. 退出 重置了 routes 重置了 路由 
    5. 按钮操作权限
      1. 先在 权限页面 以 员工页面为例 三个按钮的标识
      2. 先定义全局的getter  ponits
      3. 使用全局指令的方式  检测用户是否有对应按钮的操作权限  禁用/隐藏
      4. 在组件中使用函数的方式 处理按钮操作权限  
        
  
  
  
  -->
</body>
</html>